/* 将时间戳从 pg_time_t 转换为结构 pg_tm。 */

/*
 * 本文件属于公共领域，1996-06-05 由 Arthur David Olson 进行说明。
 *
 * 识别信息
 *	  src/timezone/localtime.c
 */

/*
 * 来自 Bradley White 的闰秒处理。
 * 来自 Guy Harris 的 POSIX 风格 TZ 环境变量处理。
 */

/* 此文件需要在前端和后端上下文中构建 */
#include "c.h"

#include <fcntl.h>

#include "datatype/timestamp.h"
#include "pgtz.h"

#include "private.h"
#include "tzfile.h"


#ifndef WILDABBR
/*
 * 有人可能会错误使用时区缩写：
 *	1.	他们可能会在调用 tzset 之前引用 tzname[0]（明确或隐式）。
 *	2.	他们可能会在调用 tzset 之前引用 tzname[1]（明确或隐式）。
 *	3.	他们可能会在设置为从不观察夏令时的时区后引用 tzname[1]。
 *	4.	他们可能会在设置为从不观察标准时间的时区后引用 tzname[0]。
 *	5.	他们可能会在调用 offtime 后引用 tm.tm_zone。
 * 在上述情况下最好的做法尚待讨论；
 * 目前，我们只是设置了一些东西，以便在这五种情况下使用 WILDABBR。
 * 另一种可能性：将 tzname[0] 初始化为字符串 "tzname[0] used before set"，对其他情况同样处理。
 * 还有一种：将 tzname[0] 初始化为 "ERA"，并在手册页面中解释此 "时区缩写" 的意义（这样做是为了让 tzname[0] 具有 "正常" 长度的三个字符）。
 */
#define WILDABBR	"   "
#endif							/* !defined WILDABBR */

static const char wildabbr[] = WILDABBR;

static const char gmt[] = "GMT";

/*
 * 如果 POSIX TZ 字符串没有规则，使用的 DST 规则。
 * 默认使用自 2017-05-07 起的美国规则。
 * POSIX 没有指定默认的 DST 规则；
 * 出于历史原因，美国规则是一个常见的默认值。
 */
#define TZDEFRULESTRING ",M3.2.0,M11.1.0"

/* 结构体 ttinfo、lsinfo、state 已移动到 pgtz.h */

enum r_type
{
	JULIAN_DAY,					/* Jn = 儒略日 */
	DAY_OF_YEAR,				/* n = 年中的第几天 */
	MONTH_NTH_DAY_OF_WEEK		/* Mm.n.d = 月、周、星期几 */
};

struct rule
{
	enum r_type r_type;			/* 规则类型 */
	int			r_day;			/* 规则的天数 */
	int			r_week;			/* 规则的周数 */
	int			r_mon;			/* 规则的月份 */
	int32		r_time;			/* 规则的过渡时间 */
};

/*
 * 静态函数的原型。
 */

static struct pg_tm *fc_gmtsub(pg_time_t const *, int32, struct pg_tm *);
static bool fc_increment_overflow(int *, int);
static bool fc_increment_overflow_time(pg_time_t *, int32);
static int64 fc_leapcorr(struct state const *, pg_time_t);
static struct pg_tm *fc_timesub(pg_time_t const *, int32, struct state const *,
							 struct pg_tm *);
static bool fc_typesequiv(struct state const *, int, int);


/*
 * X3.159-1989 第 4.12.3 节要求
 *	除了 strftime 函数，这些函数 [asctime,
 *	ctime, gmtime, localtime] 返回值为两个静态
 *	对象之一：一个分解的时间结构和一个字符数组。
 * 感谢 Paul Eggert 提出这一点。
 */

static struct pg_tm tm;

/* 初始化 *S 为基于 UTOFF、ISDST 和 DESIGIDX 的值。 */
static void fc_init_ttinfo(struct ttinfo *fc_s, int32 fc_utoff, bool fc_isdst, int fc_desigidx)
{
	fc_s->tt_utoff = fc_utoff;
	fc_s->tt_isdst = fc_isdst;
	fc_s->tt_desigidx = fc_desigidx;
	fc_s->tt_ttisstd = false;
	fc_s->tt_ttisut = false;
}

static int32 fc_detzcode(const char *const fc_codep)
{
	int32		fc_result;
	int			fc_i;
	int32		fc_one = 1;
	int32		fc_halfmaxval = fc_one << (32 - 2);
	int32		fc_maxval = fc_halfmaxval - 1 + fc_halfmaxval;
	int32		fc_minval = -1 - fc_maxval;

	fc_result = fc_codep[0] & 0x7f;
	for (fc_i = 1; fc_i < 4; ++fc_i)
		fc_result = (fc_result << 8) | (fc_codep[fc_i] & 0xff);

	if (fc_codep[0] & 0x80)
	{
		/*
		 * 即使在非二进制补码机器上也执行二进制补码取反。
		 * 如果结果为 minval - 1，则返回 minval。
		 */
		fc_result -= !TWOS_COMPLEMENT(int32) && fc_result != 0;
		fc_result += fc_minval;
	}
	return fc_result;
}

static int64 fc_detzcode64(const char *const fc_codep)
{
	uint64		fc_result;
	int			fc_i;
	int64		fc_one = 1;
	int64		fc_halfmaxval = fc_one << (64 - 2);
	int64		fc_maxval = fc_halfmaxval - 1 + fc_halfmaxval;
	int64		fc_minval = -TWOS_COMPLEMENT(int64) - fc_maxval;

	fc_result = fc_codep[0] & 0x7f;
	for (fc_i = 1; fc_i < 8; ++fc_i)
		fc_result = (fc_result << 8) | (fc_codep[fc_i] & 0xff);

	if (fc_codep[0] & 0x80)
	{
		/*
		 * 即使在非二进制补码机器上也执行二进制补码取反。
		 * 如果结果为 minval - 1，则返回 minval。
		 */
		fc_result -= !TWOS_COMPLEMENT(int64) && fc_result != 0;
		fc_result += fc_minval;
	}
	return fc_result;
}

static bool fc_differ_by_repeat(const pg_time_t fc_t1, const pg_time_t fc_t0)
{
	if (TYPE_BIT(pg_time_t) - TYPE_SIGNED(pg_time_t) < SECSPERREPEAT_BITS)
		return 0;
	return fc_t1 - fc_t0 == SECSPERREPEAT;
}

/* 从编译的 tz 文件读取的数据的输入缓冲区。 */
union input_buffer
{
	/* 缓冲区的第一部分，解释为头部。 */
	struct tzhead tzhead;

	/* 整个缓冲区。 */
	char		buf[2 * sizeof(struct tzhead) + 2 * sizeof(struct state)
					+ 4 * TZ_MAX_TIMES];
};

/* 'tzloadbody' 需要的本地存储。 */
union local_storage
{
	/* 在打开文件后分析文件内容的结果。 */
	struct file_analysis
	{
		/* 输入缓冲区。 */
		union input_buffer u;

		/* 一个用于解析文件中TZ字符串的临时状态。 */
		struct state st;
	}			u;

	/* 我们不需要“fullname”成员 */
};

/* 从名为NAME的文件中加载tz数据到*SP。 如果DOEXTEND则读取扩展格式。 使用*LSP进行临时存储。 成功时返回0，失败时返回errno值。
 * PG：如果“canonname”不为NULL，则成功时给定名称的标准拼写存储在此处（缓冲区必须大于TZ_STRLEN_MAX字节！）。
 */
static int fc_tzloadbody(char const *fc_name, char *fc_canonname, struct state *fc_sp, bool fc_doextend,
		   union local_storage *fc_lsp)
{
	int			fc_i;
	int			fc_fid;
	int			fc_stored;
	ssize_t		fc_nread;
	union input_buffer *fc_up = &fc_lsp->u.u;
	int			fc_tzheadsize = sizeof(struct tzhead);

	fc_sp->goback = fc_sp->goahead = false;

	if (!fc_name)
	{
		fc_name = TZDEFAULT;
		if (!fc_name)
			return EINVAL;
	}

	if (fc_name[0] == ':')
		++fc_name;

	fc_fid = pg_open_tzfile(fc_name, fc_canonname);
	if (fc_fid < 0)
		return ENOENT;			/* pg_open_tzfile可能不设置errno */

	fc_nread = read(fc_fid, fc_up->buf, sizeof fc_up->buf);
	if (fc_nread < fc_tzheadsize)
	{
		int			fc_err = fc_nread < 0 ? errno : EINVAL;

		close(fc_fid);
		return fc_err;
	}
	if (close(fc_fid) < 0)
		return errno;
	for (fc_stored = 4; fc_stored <= 8; fc_stored *= 2)
	{
		int32		fc_ttisstdcnt = fc_detzcode(fc_up->tzhead.tzh_ttisstdcnt);
		int32		fc_ttisutcnt = fc_detzcode(fc_up->tzhead.tzh_ttisutcnt);
		int64		fc_prevtr = 0;
		int32		fc_prevcorr = 0;
		int32		fc_leapcnt = fc_detzcode(fc_up->tzhead.tzh_leapcnt);
		int32		fc_timecnt = fc_detzcode(fc_up->tzhead.tzh_timecnt);
		int32		fc_typecnt = fc_detzcode(fc_up->tzhead.tzh_typecnt);
		int32		fc_charcnt = fc_detzcode(fc_up->tzhead.tzh_charcnt);
		char const *fc_p = fc_up->buf + fc_tzheadsize;

		/*
		 * 尽管tzfile(5)当前要求typecnt为非零，
		 * 但支持未来可能允许在包含TZ字符串且没有过渡的文件中为零typecnt的格式。
		 */
		if (!(0 <= fc_leapcnt && fc_leapcnt < TZ_MAX_LEAPS
			  && 0 <= fc_typecnt && fc_typecnt < TZ_MAX_TYPES
			  && 0 <= fc_timecnt && fc_timecnt < TZ_MAX_TIMES
			  && 0 <= fc_charcnt && fc_charcnt < TZ_MAX_CHARS
			  && (fc_ttisstdcnt == fc_typecnt || fc_ttisstdcnt == 0)
			  && (fc_ttisutcnt == fc_typecnt || fc_ttisutcnt == 0)))
			return EINVAL;
		if (fc_nread
			< (fc_tzheadsize		/* struct tzhead */
			   + fc_timecnt * fc_stored	/* ats */
			   + fc_timecnt		/* types */
			   + fc_typecnt * 6	/* ttinfos */
			   + fc_charcnt		/* chars */
			   + fc_leapcnt * (fc_stored + 4) /* lsinfos */
			   + fc_ttisstdcnt		/* ttisstds */
			   + fc_ttisutcnt))	/* ttisuts */
			return EINVAL;
		fc_sp->leapcnt = fc_leapcnt;
		fc_sp->timecnt = fc_timecnt;
		fc_sp->typecnt = fc_typecnt;
		fc_sp->charcnt = fc_charcnt;

		/*
		 * 读取过渡，丢弃超出pg_time_t范围的过渡。但
		 * 假装TIME_T_MIN之前的最后一次过渡发生在
		 * TIME_T_MIN。
		 */
		fc_timecnt = 0;
		for (fc_i = 0; fc_i < fc_sp->timecnt; ++fc_i)
		{
			int64		fc_at
			= fc_stored == 4 ? fc_detzcode(fc_p) : fc_detzcode64(fc_p);

			fc_sp->types[fc_i] = fc_at <= TIME_T_MAX;
			if (fc_sp->types[fc_i])
			{
				pg_time_t	fc_attime
				= ((TYPE_SIGNED(pg_time_t) ? fc_at < TIME_T_MIN : fc_at < 0)
				   ? TIME_T_MIN : fc_at);

				if (fc_timecnt && fc_attime <= fc_sp->ats[fc_timecnt - 1])
				{
					if (fc_attime < fc_sp->ats[fc_timecnt - 1])
						return EINVAL;
					fc_sp->types[fc_i - 1] = 0;
					fc_timecnt--;
				}
				fc_sp->ats[fc_timecnt++] = fc_attime;
			}
			fc_p += fc_stored;
		}

		fc_timecnt = 0;
		for (fc_i = 0; fc_i < fc_sp->timecnt; ++fc_i)
		{
			unsigned char fc_typ = *fc_p++;

			if (fc_sp->typecnt <= fc_typ)
				return EINVAL;
			if (fc_sp->types[fc_i])
				fc_sp->types[fc_timecnt++] = fc_typ;
		}
		fc_sp->timecnt = fc_timecnt;
		for (fc_i = 0; fc_i < fc_sp->typecnt; ++fc_i)
		{
			struct ttinfo *fc_ttisp;
			unsigned char fc_isdst,
						fc_desigidx;

			fc_ttisp = &fc_sp->ttis[fc_i];
			fc_ttisp->tt_utoff = fc_detzcode(fc_p);
			fc_p += 4;
			fc_isdst = *fc_p++;
			if (!(fc_isdst < 2))
				return EINVAL;
			fc_ttisp->tt_isdst = fc_isdst;
			fc_desigidx = *fc_p++;
			if (!(fc_desigidx < fc_sp->charcnt))
				return EINVAL;
			fc_ttisp->tt_desigidx = fc_desigidx;
		}
		for (fc_i = 0; fc_i < fc_sp->charcnt; ++fc_i)
			fc_sp->chars[fc_i] = *fc_p++;
		fc_sp->chars[fc_i] = '\0';	/* 确保末尾有'\0' */

		/* 读取闰秒，丢弃超出pg_time_t范围的过渡。 */
		fc_leapcnt = 0;
		for (fc_i = 0; fc_i < fc_sp->leapcnt; ++fc_i)
		{
			int64		fc_tr = fc_stored == 4 ? fc_detzcode(fc_p) : fc_detzcode64(fc_p);
			int32		fc_corr = fc_detzcode(fc_p + fc_stored);

			fc_p += fc_stored + 4;
			/* 闰秒不能发生在纪元之前。 */
			if (fc_tr < 0)
				return EINVAL;
			if (fc_tr <= TIME_T_MAX)
			{
				/*
				 * 闰秒每个UTC月不能出现超过一次，
				 * 而UTC月份长度至少为28天（减去1秒用于
				 * 负闰秒）。 每个闰秒的修正必须
				 * 与前一个的差异为1秒。
				 */
				if (fc_tr - fc_prevtr < 28 * SECSPERDAY - 1
					|| (fc_corr != fc_prevcorr - 1 && fc_corr != fc_prevcorr + 1))
					return EINVAL;
				fc_sp->lsis[fc_leapcnt].ls_trans = fc_prevtr = fc_tr;
				fc_sp->lsis[fc_leapcnt].ls_corr = fc_prevcorr = fc_corr;
				fc_leapcnt++;
			}
		}
		fc_sp->leapcnt = fc_leapcnt;

		for (fc_i = 0; fc_i < fc_sp->typecnt; ++fc_i)
		{
			struct ttinfo *fc_ttisp;

			fc_ttisp = &fc_sp->ttis[fc_i];
			if (fc_ttisstdcnt == 0)
				fc_ttisp->tt_ttisstd = false;
			else
			{
				if (*fc_p != true && *fc_p != false)
					return EINVAL;
				fc_ttisp->tt_ttisstd = *fc_p++;
			}
		}
		for (fc_i = 0; fc_i < fc_sp->typecnt; ++fc_i)
		{
			struct ttinfo *fc_ttisp;

			fc_ttisp = &fc_sp->ttis[fc_i];
			if (fc_ttisutcnt == 0)
				fc_ttisp->tt_ttisut = false;
			else
			{
				if (*fc_p != true && *fc_p != false)
					return EINVAL;
				fc_ttisp->tt_ttisut = *fc_p++;
			}
		}

		/*
		 * 如果这是一个旧文件，我们就完成了。
		 */
		if (fc_up->tzhead.tzh_version[0] == '\0')
			break;
		fc_nread -= fc_p - fc_up->buf;
		memmove(fc_up->buf, fc_p, fc_nread);
	}
	if (fc_doextend && fc_nread > 2 &&
		fc_up->buf[0] == '\n' && fc_up->buf[fc_nread - 1] == '\n' &&
		fc_sp->typecnt + 2 <= TZ_MAX_TYPES)
	{
		struct state *fc_ts = &fc_lsp->u.st;

		fc_up->buf[fc_nread - 1] = '\0';
		if (tzparse(&fc_up->buf[1], fc_ts, false))
		{
			/*
			 * 尝试重用现有的缩写。 没有这一点，
			 * America/Anchorage在2037年时将处于边缘，因为
			 * TZ_MAX_CHARS为50，sp->charcnt等于40（对应于
			 * LMT AST AWT APT AHST AHDT YST AKDT AKST）而ts->charcnt等于10（对应于
			 * AKST AKDT）。 重用意味着sp->charcnt在这个
			 * 示例中可以保持为40。
			 */
			int			fc_gotabbr = 0;
			int			fc_charcnt = fc_sp->charcnt;

			for (fc_i = 0; fc_i < fc_ts->typecnt; fc_i++)
			{
				char	   *fc_tsabbr = fc_ts->chars + fc_ts->ttis[fc_i].tt_desigidx;
				int			fc_j;

				for (fc_j = 0; fc_j < fc_charcnt; fc_j++)
					if (strcmp(fc_sp->chars + fc_j, fc_tsabbr) == 0)
					{
						fc_ts->ttis[fc_i].tt_desigidx = fc_j;
						fc_gotabbr++;
						break;
					}
				if (!(fc_j < fc_charcnt))
				{
					int			fc_tsabbrlen = strlen(fc_tsabbr);

					if (fc_j + fc_tsabbrlen < TZ_MAX_CHARS)
					{
						strcpy(fc_sp->chars + fc_j, fc_tsabbr);
						fc_charcnt = fc_j + fc_tsabbrlen + 1;
						fc_ts->ttis[fc_i].tt_desigidx = fc_j;
						fc_gotabbr++;
					}
				}
			}
			if (fc_gotabbr == fc_ts->typecnt)
			{
				fc_sp->charcnt = fc_charcnt;

				/*
				 * 忽略zic生成的任何尾随的无操作过渡，因为
				 * 它们在这里没有帮助，并可能导致zic 2016j
				 * 或更早版本中的bug。
				 */
				while (1 < fc_sp->timecnt
					   && (fc_sp->types[fc_sp->timecnt - 1]
						   == fc_sp->types[fc_sp->timecnt - 2]))
					fc_sp->timecnt--;

				for (fc_i = 0; fc_i < fc_ts->timecnt; fc_i++)
					if (fc_sp->timecnt == 0
						|| (fc_sp->ats[fc_sp->timecnt - 1]
							< fc_ts->ats[fc_i] + fc_leapcorr(fc_sp, fc_ts->ats[fc_i])))
						break;
				while (fc_i < fc_ts->timecnt
					   && fc_sp->timecnt < TZ_MAX_TIMES)
				{
					fc_sp->ats[fc_sp->timecnt]
						= fc_ts->ats[fc_i] + fc_leapcorr(fc_sp, fc_ts->ats[fc_i]);
					fc_sp->types[fc_sp->timecnt] = (fc_sp->typecnt
											  + fc_ts->types[fc_i]);
					fc_sp->timecnt++;
					fc_i++;
				}
				for (fc_i = 0; fc_i < fc_ts->typecnt; fc_i++)
					fc_sp->ttis[fc_sp->typecnt++] = fc_ts->ttis[fc_i];
			}
		}
	}
	if (fc_sp->typecnt == 0)
		return EINVAL;
	if (fc_sp->timecnt > 1)
	{
		for (fc_i = 1; fc_i < fc_sp->timecnt; ++fc_i)
			if (fc_typesequiv(fc_sp, fc_sp->types[fc_i], fc_sp->types[0]) &&
				fc_differ_by_repeat(fc_sp->ats[fc_i], fc_sp->ats[0]))
			{
				fc_sp->goback = true;
				break;
			}
		for (fc_i = fc_sp->timecnt - 2; fc_i >= 0; --fc_i)
			if (fc_typesequiv(fc_sp, fc_sp->types[fc_sp->timecnt - 1],
						   fc_sp->types[fc_i]) &&
				fc_differ_by_repeat(fc_sp->ats[fc_sp->timecnt - 1],
								 fc_sp->ats[fc_i]))
			{
				fc_sp->goahead = true;
				break;
			}
	}

	/*
	 * 从数据推断sp->defaulttype。 尽管对于最近的tzdb发布，
	 * 此默认类型总是为零，但对于tzdb 2018e或更早版本的数据情况更为复杂。
	 *
	 * 第一组启发式方法解决了tzdb 2013c或更早版生成的32位数据中的bug。
	 * 该解决方法适用于像Australia/Macquarie这样的区域，其中在第一次过渡之前的时间戳
	 * 具有不是最早标准时间类型的时间类型。 参见：
	 * https://mm.icann.org/pipermail/tz/2013-May/019368.html
	 */

	/*
	 * 如果类型0在过渡中未使用，则这是早期
	 * 时间使用的类型。
	 */
	for (fc_i = 0; fc_i < fc_sp->timecnt; ++fc_i)
		if (fc_sp->types[fc_i] == 0)
			break;
	fc_i = fc_i < fc_sp->timecnt ? -1 : 0;

	/*
	 * 如果没有上述情况，且存在过渡时间，
	 * 并且第一次过渡为昼时，则查找标准类型
	 * 小于且最接近第一次过渡类型。
	 */
	if (fc_i < 0 && fc_sp->timecnt > 0 && fc_sp->ttis[fc_sp->types[0]].tt_isdst)
	{
		fc_i = fc_sp->types[0];
		while (--fc_i >= 0)
			if (!fc_sp->ttis[fc_i].tt_isdst)
				break;
	}

	/*
	 * 接下来的启发式方法适用于2018e或更早生成的数据，
	 * 例如EST5EDT，那里第一个转换是进入夏令时。
	 */

	/*
	 * 如果尚未得到结果，找到第一个标准类型。如果没有，则返回
	 * 类型零。
	 */
	if (fc_i < 0)
	{
		fc_i = 0;
		while (fc_sp->ttis[fc_i].tt_isdst)
			if (++fc_i >= fc_sp->typecnt)
			{
				fc_i = 0;
				break;
			}
	}

	/*
	 * 如果我们不需要担心2018e或更早的数据，简单的'sp->defaulttype = 0;'就足够了。
	 * 更简单的方法是删除defaulttype成员，直接使用0替代。
	 */
	fc_sp->defaulttype = fc_i;

	return 0;
}

/* 从名为NAME的文件加载时区数据到*SP。如果DOEXTEND为真，则读取扩展
 * 格式。成功返回0，失败返回errno值。
 * PG：如果"canonname"不为NULL，则在成功时，给定名称的规范拼写将存储在这里（缓冲区必须大于TZ_STRLEN_MAX字节！）。
 */
int tzload(const char *fc_name, char *fc_canonname, struct state *fc_sp, bool fc_doextend)
{
	union local_storage *fc_lsp = malloc(sizeof *fc_lsp);

	if (!fc_lsp)
		return errno;
	else
	{
		int			fc_err = fc_tzloadbody(fc_name, fc_canonname, fc_sp, fc_doextend, fc_lsp);

		free(fc_lsp);
		return fc_err;
	}
}

static bool fc_typesequiv(const struct state *fc_sp, int fc_a, int fc_b)
{
	bool		fc_result;

	if (fc_sp == NULL ||
		fc_a < 0 || fc_a >= fc_sp->typecnt ||
		fc_b < 0 || fc_b >= fc_sp->typecnt)
		fc_result = false;
	else
	{
		const struct ttinfo *fc_ap = &fc_sp->ttis[fc_a];
		const struct ttinfo *fc_bp = &fc_sp->ttis[fc_b];

		fc_result = (fc_ap->tt_utoff == fc_bp->tt_utoff
				  && fc_ap->tt_isdst == fc_bp->tt_isdst
				  && fc_ap->tt_ttisstd == fc_bp->tt_ttisstd
				  && fc_ap->tt_ttisut == fc_bp->tt_ttisut
				  && (strcmp(&fc_sp->chars[fc_ap->tt_desigidx],
							 &fc_sp->chars[fc_bp->tt_desigidx])
					  == 0));
	}
	return fc_result;
}

static const int mon_lengths[2][MONSPERYEAR] = {
	{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
	{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};

static const int year_lengths[2] = {
	DAYSPERNYEAR, DAYSPERLYEAR
};

/*
 * 给定一个指向时区字符串的指针，搜索直到找到一个不是
 * 时区缩写中有效字符的字符。
 * 返回指向该字符的指针。
 */

static const char * fc_getzname(const char *fc_strp)
{
	char		fc_c;

	while ((fc_c = *fc_strp) != '\0' && !is_digit(fc_c) && fc_c != ',' && fc_c != '-' &&
		   fc_c != '+')
		++fc_strp;
	return fc_strp;
}

/*
 * 给定一个指向扩展时区字符串的指针，扫描直到找到时区
 * 缩写的结束分隔符。
 * 返回指向分隔符的指针。
 *
 * 与上面的getzname一样，合法字符集实际上是相当
 * 有限的，其他字符会产生未定义结果。
 * 我们这里不进行任何检查；检查在稍后的常见代码中进行。
 */

static const char * fc_getqzname(const char *fc_strp, const int fc_delim)
{
	int			fc_c;

	while ((fc_c = *fc_strp) != '\0' && fc_c != fc_delim)
		++fc_strp;
	return fc_strp;
}

/*
 * 给定一个指向时区字符串的指针，从该字符串中提取一个数字。
 * 检查数字是否在指定范围内；如果不在，返回
 * NULL。
 * 否则，返回指向第一个不属于该数字的字符的指针。
 */

static const char * fc_getnum(const char *fc_strp, int *const fc_nump, const int fc_min, const int fc_max)
{
	char		fc_c;
	int			fc_num;

	if (fc_strp == NULL || !is_digit(fc_c = *fc_strp))
		return NULL;
	fc_num = 0;
	do
	{
		fc_num = fc_num * 10 + (fc_c - '0');
		if (fc_num > fc_max)
			return NULL;		/* 非法值 */
		fc_c = *++fc_strp;
	} while (is_digit(fc_c));
	if (fc_num < fc_min)
		return NULL;			/* 非法值 */
	*fc_nump = fc_num;
	return fc_strp;
}

/*
 * 给定一个指向时区字符串的指针，从该字符串中提取以
 * [+-]hh[:mm[:ss]] 形式表示的秒数。
 * 如果发生任何错误，返回NULL。
 * 否则，返回指向第一个不属于秒数的字符的指针。
 */

static const char * fc_getsecs(const char *fc_strp, int32 *const fc_secsp)
{
	int			fc_num;

	/*
	 * 'HOURSPERDAY * DAYSPERWEEK - 1' 允许类似于
	 * "M10.4.6/26"的准Posix规则，该规则不符合Posix，
	 * 但指定的是"10月23日或之后的第一个星期日的02:00"的等效时间。
	 */
	fc_strp = fc_getnum(fc_strp, &fc_num, 0, HOURSPERDAY * DAYSPERWEEK - 1);
	if (fc_strp == NULL)
		return NULL;
	*fc_secsp = fc_num * (int32) SECSPERHOUR;
	if (*fc_strp == ':')
	{
		++fc_strp;
		fc_strp = fc_getnum(fc_strp, &fc_num, 0, MINSPERHOUR - 1);
		if (fc_strp == NULL)
			return NULL;
		*fc_secsp += fc_num * SECSPERMIN;
		if (*fc_strp == ':')
		{
			++fc_strp;
			/* 'SECSPERMIN' 允许闰秒。 */
			fc_strp = fc_getnum(fc_strp, &fc_num, 0, SECSPERMIN);
			if (fc_strp == NULL)
				return NULL;
			*fc_secsp += fc_num;
		}
	}
	return fc_strp;
}

/*
 * 给定一个指向时区字符串的指针，从该字符串中提取一个偏移量，
 * 以[+-]hh[:mm[:ss]] 形式表示。
 * 如果发生任何错误，返回NULL。
 * 否则，返回指向第一个不属于时间的字符的指针。
 */

static const char * fc_getoffset(const char *fc_strp, int32 *const fc_offsetp)
{
	bool		fc_neg = false;

	if (*fc_strp == '-')
	{
		fc_neg = true;
		++fc_strp;
	}
	else if (*fc_strp == '+')
		++fc_strp;
	fc_strp = fc_getsecs(fc_strp, fc_offsetp);
	if (fc_strp == NULL)
		return NULL;			/* 非法时间 */
	if (fc_neg)
		*fc_offsetp = -*fc_offsetp;
	return fc_strp;
}

/*
 * 给定一个指向时区字符串的指针，以日期[/时间]的形式提取一个规则。
 * 请参见POSIX第8节以了解“日期”和“时间”的格式。
 * 如果未找到有效规则，返回NULL。
 * 否则，返回指向第一个不属于该规则的字符的指针。
 */

static const char * fc_getrule(const char *fc_strp, struct rule *const fc_rulep)
{
	if (*fc_strp == 'J')
	{
		/*
		 * 儒略日。
		 */
		fc_rulep->r_type = JULIAN_DAY;
		++fc_strp;
		fc_strp = fc_getnum(fc_strp, &fc_rulep->r_day, 1, DAYSPERNYEAR);
	}
	else if (*fc_strp == 'M')
	{
		/*
		 * 月，周，日。
		 */
		fc_rulep->r_type = MONTH_NTH_DAY_OF_WEEK;
		++fc_strp;
		fc_strp = fc_getnum(fc_strp, &fc_rulep->r_mon, 1, MONSPERYEAR);
		if (fc_strp == NULL)
			return NULL;
		if (*fc_strp++ != '.')
			return NULL;
		fc_strp = fc_getnum(fc_strp, &fc_rulep->r_week, 1, 5);
		if (fc_strp == NULL)
			return NULL;
		if (*fc_strp++ != '.')
			return NULL;
		fc_strp = fc_getnum(fc_strp, &fc_rulep->r_day, 0, DAYSPERWEEK - 1);
	}
	else if (is_digit(*fc_strp))
	{
		/*
		 * 年中的第几天。
		 */
		fc_rulep->r_type = DAY_OF_YEAR;
		fc_strp = fc_getnum(fc_strp, &fc_rulep->r_day, 0, DAYSPERLYEAR - 1);
	}
	else
		return NULL;			/* 格式无效 */
	if (fc_strp == NULL)
		return NULL;
	if (*fc_strp == '/')
	{
		/*
		 * 指定的时间。
		 */
		++fc_strp;
		fc_strp = fc_getoffset(fc_strp, &fc_rulep->r_time);
	}
	else
		fc_rulep->r_time = 2 * SECSPERHOUR;	/* 默认 = 2:00:00 */
	return fc_strp;
}

/*
 * 给定一个年份、一条规则以及该规则生效时的UT偏移量，计算该规则生效的年相对时间。
 */

static int32 fc_transtime(const int fc_year, const struct rule *const fc_rulep,
		  const int32 fc_offset)
{
	bool		fc_leapyear;
	int32		fc_value;
	int			fc_i;
	int			fc_d,
				fc_m1,
				fc_yy0,
				fc_yy1,
				fc_yy2,
				fc_dow;

	INITIALIZE(fc_value);
	fc_leapyear = isleap(fc_year);
	switch (fc_rulep->r_type)
	{

		case JULIAN_DAY:

			/*
			 * Jn - Julian天数，1表示1月1日，60表示3月1日，即使在闰年中也是如此。在非闰年中，或者如果天数为59或更少，仅需将SECSPERDAY乘以天数减1后加到1月1日午夜的时间，以获得该天的时间。
			 */
			fc_value = (fc_rulep->r_day - 1) * SECSPERDAY;
			if (fc_leapyear && fc_rulep->r_day >= 60)
				fc_value += SECSPERDAY;
			break;

		case DAY_OF_YEAR:

			/*
			 * n - 年中的天数。只需将SECSPERDAY乘以天数加到1月1日午夜的时间，以获得该天的时间。
			 */
			fc_value = fc_rulep->r_day * SECSPERDAY;
			break;

		case MONTH_NTH_DAY_OF_WEEK:

			/*
			 * Mm.n.d - 第m月的第d天。
			 */

			/*
			 * 使用Zeller's Congruence来获取该月份第一天的星期几。
			 */
			fc_m1 = (fc_rulep->r_mon + 9) % 12 + 1;
			fc_yy0 = (fc_rulep->r_mon <= 2) ? (fc_year - 1) : fc_year;
			fc_yy1 = fc_yy0 / 100;
			fc_yy2 = fc_yy0 % 100;
			fc_dow = ((26 * fc_m1 - 2) / 10 +
				   1 + fc_yy2 + fc_yy2 / 4 + fc_yy1 / 4 - 2 * fc_yy1) % 7;
			if (fc_dow < 0)
				fc_dow += DAYSPERWEEK;

			/*
			 * "dow"是该月份第一天的星期几。获取该月份第一个"dow"日的天数（从零开始）。
			 */
			fc_d = fc_rulep->r_day - fc_dow;
			if (fc_d < 0)
				fc_d += DAYSPERWEEK;
			for (fc_i = 1; fc_i < fc_rulep->r_week; ++fc_i)
			{
				if (fc_d + DAYSPERWEEK >=
					mon_lengths[(int) fc_leapyear][fc_rulep->r_mon - 1])
					break;
				fc_d += DAYSPERWEEK;
			}

			/*
			 * "d"是我们想要的那一天的天数（从零开始）。
			 */
			fc_value = fc_d * SECSPERDAY;
			for (fc_i = 0; fc_i < fc_rulep->r_mon - 1; ++fc_i)
				fc_value += mon_lengths[(int) fc_leapyear][fc_i] * SECSPERDAY;
			break;
	}

	/*
	 * "value"是所讨论日期00:00:00 UT的年相对时间。要获取当天指定本地时间的年相对时间，请添加过渡时间和当前UT偏移量。
	 */
	return fc_value + fc_rulep->r_time + fc_offset;
}

/*
 * 给定一个POSIX第8节风格的TZ字符串，按需填充规则表。
 * 成功时返回true，失败时返回false。
 */
bool tzparse(const char *fc_name, struct state *fc_sp, bool fc_lastditch)
{
	const char *fc_stdname;
	const char *fc_dstname = NULL;
	size_t		fc_stdlen;
	size_t		fc_dstlen;
	size_t		fc_charcnt;
	int32		fc_stdoffset;
	int32		fc_dstoffset;
	char	   *fc_cp;
	bool		fc_load_ok;

	fc_stdname = fc_name;
	if (fc_lastditch)
	{
		/* 与IANA不同，不假设名称完全是"GTM" */
		fc_stdlen = strlen(fc_name);	/* 标准时区名称的长度 */
		fc_name += fc_stdlen;
		fc_stdoffset = 0;
	}
	else
	{
		if (*fc_name == '<')
		{
			fc_name++;
			fc_stdname = fc_name;
			fc_name = fc_getqzname(fc_name, '>');
			if (*fc_name != '>')
				return false;
			fc_stdlen = fc_name - fc_stdname;
			fc_name++;
		}
		else
		{
			fc_name = fc_getzname(fc_name);
			fc_stdlen = fc_name - fc_stdname;
		}
		if (*fc_name == '\0')		/* 我们允许空的STD缩写，这与IANA不同 */
			return false;
		fc_name = fc_getoffset(fc_name, &fc_stdoffset);
		if (fc_name == NULL)
			return false;
	}
	fc_charcnt = fc_stdlen + 1;
	if (sizeof fc_sp->chars < fc_charcnt)
		return false;

	/*
	 * IANA代码在这里总是尝试tzload(TZDEFRULES)。我们不想这样做；在最后的情况下，这将是坏消息，而此时我们不能假设pg_open_tzfile()是合理的。此外，如果我们加载了它并且它包含依赖于闰秒的信息，那也会导致问题。最后，IANA已经弃用了TZDEFRULES功能，因此它可能在某个时刻会消亡。现在不支持它似乎是良好的未来防护措施。
	 */
	fc_load_ok = false;
	fc_sp->goback = fc_sp->goahead = false;	/* 模拟失败的tzload() */
	fc_sp->leapcnt = 0;			/* 故意假设没有闰秒 */

	if (*fc_name != '\0')
	{
		if (*fc_name == '<')
		{
			fc_dstname = ++fc_name;
			fc_name = fc_getqzname(fc_name, '>');
			if (*fc_name != '>')
				return false;
			fc_dstlen = fc_name - fc_dstname;
			fc_name++;
		}
		else
		{
			fc_dstname = fc_name;
			fc_name = fc_getzname(fc_name);
			fc_dstlen = fc_name - fc_dstname;	/* DST缩写的长度 */
		}
		if (!fc_dstlen)
			return false;
		fc_charcnt += fc_dstlen + 1;
		if (sizeof fc_sp->chars < fc_charcnt)
			return false;
		if (*fc_name != '\0' && *fc_name != ',' && *fc_name != ';')
		{
			fc_name = fc_getoffset(fc_name, &fc_dstoffset);
			if (fc_name == NULL)
				return false;
		}
		else
			fc_dstoffset = fc_stdoffset - SECSPERHOUR;
		if (*fc_name == '\0' && !fc_load_ok)
			fc_name = TZDEFRULESTRING;
		if (*fc_name == ',' || *fc_name == ';')
		{
			struct rule fc_start;
			struct rule fc_end;
			int			fc_year;
			int			fc_yearlim;
			int			fc_timecnt;
			pg_time_t	fc_janfirst;
			int32		fc_janoffset = 0;
			int			fc_yearbeg;

			++fc_name;
			if ((fc_name = fc_getrule(fc_name, &fc_start)) == NULL)
				return false;
			if (*fc_name++ != ',')
				return false;
			if ((fc_name = fc_getrule(fc_name, &fc_end)) == NULL)
				return false;
			if (*fc_name != '\0')
				return false;
			fc_sp->typecnt = 2;	/* 标准时间和夏令时 */

			/*
			 * 每年两个过渡，从EPOCH_YEAR开始。
			 */
			fc_init_ttinfo(&fc_sp->ttis[0], -fc_stdoffset, false, 0);
			fc_init_ttinfo(&fc_sp->ttis[1], -fc_dstoffset, true, fc_stdlen + 1);
			fc_sp->defaulttype = 0;
			fc_timecnt = 0;
			fc_janfirst = 0;
			fc_yearbeg = EPOCH_YEAR;

			do
			{
				int32		fc_yearsecs
				= year_lengths[isleap(fc_yearbeg - 1)] * SECSPERDAY;

				fc_yearbeg--;
				if (fc_increment_overflow_time(&fc_janfirst, -fc_yearsecs))
				{
					fc_janoffset = -fc_yearsecs;
					break;
				}
			} while (EPOCH_YEAR - YEARSPERREPEAT / 2 < fc_yearbeg);

			fc_yearlim = fc_yearbeg + YEARSPERREPEAT + 1;
			for (fc_year = fc_yearbeg; fc_year < fc_yearlim; fc_year++)
			{
				int32
							fc_starttime = fc_transtime(fc_year, &fc_start, fc_stdoffset),
							fc_endtime = fc_transtime(fc_year, &fc_end, fc_dstoffset);
				int32
							fc_yearsecs = (year_lengths[isleap(fc_year)]
										* SECSPERDAY);
				bool		fc_reversed = fc_endtime < fc_starttime;

				if (fc_reversed)
				{
					int32		fc_swap = fc_starttime;

					fc_starttime = fc_endtime;
					fc_endtime = fc_swap;
				}
				if (fc_reversed
					|| (fc_starttime < fc_endtime
						&& (fc_endtime - fc_starttime
							< (fc_yearsecs
							   + (fc_stdoffset - fc_dstoffset)))))
				{
					if (TZ_MAX_TIMES - 2 < fc_timecnt)
						break;
					fc_sp->ats[fc_timecnt] = fc_janfirst;
					if (!fc_increment_overflow_time
						(&fc_sp->ats[fc_timecnt],
						 fc_janoffset + fc_starttime))
						fc_sp->types[fc_timecnt++] = !fc_reversed;
					fc_sp->ats[fc_timecnt] = fc_janfirst;
					if (!fc_increment_overflow_time
						(&fc_sp->ats[fc_timecnt],
						 fc_janoffset + fc_endtime))
					{
						fc_sp->types[fc_timecnt++] = fc_reversed;
						fc_yearlim = fc_year + YEARSPERREPEAT + 1;
					}
				}
				if (fc_increment_overflow_time
					(&fc_janfirst, fc_janoffset + fc_yearsecs))
					break;
				fc_janoffset = 0;
			}
			fc_sp->timecnt = fc_timecnt;
			if (!fc_timecnt)
			{
				fc_sp->ttis[0] = fc_sp->ttis[1];
				fc_sp->typecnt = 1;	/* 永久夏令时。 */
			}
			else if (YEARSPERREPEAT < fc_year - fc_yearbeg)
				fc_sp->goback = fc_sp->goahead = true;
		}
		else
		{
			int32		fc_theirstdoffset;
			int32		fc_theirdstoffset;
			int32		fc_theiroffset;
			bool		fc_isdst;
			int			fc_i;
			int			fc_j;

			if (*fc_name != '\0')
				return false;

			/*
			 * theirstdoffset和theirdstoffset的初始值。
			 */
			fc_theirstdoffset = 0;
			for (fc_i = 0; fc_i < fc_sp->timecnt; ++fc_i)
			{
				fc_j = fc_sp->types[fc_i];
				if (!fc_sp->ttis[fc_j].tt_isdst)
				{
					fc_theirstdoffset =
						-fc_sp->ttis[fc_j].tt_utoff;
					break;
				}
			}
			fc_theirdstoffset = 0;
			for (fc_i = 0; fc_i < fc_sp->timecnt; ++fc_i)
			{
				fc_j = fc_sp->types[fc_i];
				if (fc_sp->ttis[fc_j].tt_isdst)
				{
					fc_theirdstoffset =
						-fc_sp->ttis[fc_j].tt_utoff;
					break;
				}
			}

			/*
			 * 最初假设我们处于标准时间。
			 */
			fc_isdst = false;
			fc_theiroffset = fc_theirstdoffset;

			/*
			 * 现在在跟踪偏移量时，调整过渡时间和类型。
			 */
			for (fc_i = 0; fc_i < fc_sp->timecnt; ++fc_i)
			{
				fc_j = fc_sp->types[fc_i];
				fc_sp->types[fc_i] = fc_sp->ttis[fc_j].tt_isdst;
				if (fc_sp->ttis[fc_j].tt_ttisut)
				{
					/* 不调整过渡时间 */
				}
				else
				{
					/*
					 * 如果夏令时生效，并且过渡时间没有指定为标准时间，则将夏令时偏移量加到过渡时间；否则，将标准时间偏移量加到过渡时间。
					 */
					/*
					 * 从DST到DDST的过渡将有效消失，因为POSIX只提供一个DST偏移量。
					 */
					if (fc_isdst && !fc_sp->ttis[fc_j].tt_ttisstd)
					{
						fc_sp->ats[fc_i] += fc_dstoffset -
							fc_theirdstoffset;
					}
					else
					{
						fc_sp->ats[fc_i] += fc_stdoffset -
							fc_theirstdoffset;
					}
				}
				fc_theiroffset = -fc_sp->ttis[fc_j].tt_utoff;
				if (fc_sp->ttis[fc_j].tt_isdst)
					fc_theirdstoffset = fc_theiroffset;
				else
					fc_theirstdoffset = fc_theiroffset;
			}

			/*
			 * 最后，填充ttis。
			 */
			fc_init_ttinfo(&fc_sp->ttis[0], -fc_stdoffset, false, 0);
			fc_init_ttinfo(&fc_sp->ttis[1], -fc_dstoffset, true, fc_stdlen + 1);
			fc_sp->typecnt = 2;
			fc_sp->defaulttype = 0;
		}
	}
	else
	{
		fc_dstlen = 0;
		fc_sp->typecnt = 1;		/* 仅标准时间 */
		fc_sp->timecnt = 0;
		fc_init_ttinfo(&fc_sp->ttis[0], -fc_stdoffset, false, 0);
		fc_sp->defaulttype = 0;
	}
	fc_sp->charcnt = fc_charcnt;
	fc_cp = fc_sp->chars;
	memcpy(fc_cp, fc_stdname, fc_stdlen);
	fc_cp += fc_stdlen;
	*fc_cp++ = '\0';
	if (fc_dstlen != 0)
	{
		memcpy(fc_cp, fc_dstname, fc_dstlen);
		*(fc_cp + fc_dstlen) = '\0';
	}
	return true;
}

static void fc_gmtload(struct state *const fc_sp)
{
	if (tzload(gmt, NULL, fc_sp, true) != 0)
		tzparse(gmt, fc_sp, true);
}


/*
 * 处理“好像没有库函数调用”的简单方法
 * 是不调用它，因此我们将其内部实现放入“localsub”，可以
 * 自由调用。（不，PANS并不要求上述行为，
 * 但确实是理想的。）
 */
static struct pg_tm * fc_localsub(struct state const *fc_sp, pg_time_t const *fc_timep,
		 struct pg_tm *const fc_tmp)
{
	const struct ttinfo *fc_ttisp;
	int			fc_i;
	struct pg_tm *fc_result;
	const pg_time_t fc_t = *fc_timep;

	if (fc_sp == NULL)
		return fc_gmtsub(fc_timep, 0, fc_tmp);
	if ((fc_sp->goback && fc_t < fc_sp->ats[0]) ||
		(fc_sp->goahead && fc_t > fc_sp->ats[fc_sp->timecnt - 1]))
	{
		pg_time_t	fc_newt = fc_t;
		pg_time_t	fc_seconds;
		pg_time_t	fc_years;

		if (fc_t < fc_sp->ats[0])
			fc_seconds = fc_sp->ats[0] - fc_t;
		else
			fc_seconds = fc_t - fc_sp->ats[fc_sp->timecnt - 1];
		--fc_seconds;
		fc_years = (fc_seconds / SECSPERREPEAT + 1) * YEARSPERREPEAT;
		fc_seconds = fc_years * AVGSECSPERYEAR;
		if (fc_t < fc_sp->ats[0])
			fc_newt += fc_seconds;
		else
			fc_newt -= fc_seconds;
		if (fc_newt < fc_sp->ats[0] ||
			fc_newt > fc_sp->ats[fc_sp->timecnt - 1])
			return NULL;		/* "无法发生" */
		fc_result = fc_localsub(fc_sp, &fc_newt, fc_tmp);
		if (fc_result)
		{
			int64		fc_newy;

			fc_newy = fc_result->tm_year;
			if (fc_t < fc_sp->ats[0])
				fc_newy -= fc_years;
			else
				fc_newy += fc_years;
			if (!(INT_MIN <= fc_newy && fc_newy <= INT_MAX))
				return NULL;
			fc_result->tm_year = fc_newy;
		}
		return fc_result;
	}
	if (fc_sp->timecnt == 0 || fc_t < fc_sp->ats[0])
	{
		fc_i = fc_sp->defaulttype;
	}
	else
	{
		int			fc_lo = 1;
		int			fc_hi = fc_sp->timecnt;

		while (fc_lo < fc_hi)
		{
			int			fc_mid = (fc_lo + fc_hi) >> 1;

			if (fc_t < fc_sp->ats[fc_mid])
				fc_hi = fc_mid;
			else
				fc_lo = fc_mid + 1;
		}
		fc_i = (int) fc_sp->types[fc_lo - 1];
	}
	fc_ttisp = &fc_sp->ttis[fc_i];

	/*
	 * 若要获得与System V Release 2.0兼容的（错误）行为，
	 * 则应将以下语句替换为 t += ttisp->tt_utoff;
	 * timesub(&t, 0L, sp, tmp);
	 */
	fc_result = fc_timesub(&fc_t, fc_ttisp->tt_utoff, fc_sp, fc_tmp);
	if (fc_result)
	{
		fc_result->tm_isdst = fc_ttisp->tt_isdst;
		fc_result->tm_zone = unconstify(char *, &fc_sp->chars[fc_ttisp->tt_desigidx]);
	}
	return fc_result;
}


struct pg_tm *
pg_localtime(const pg_time_t *fc_timep, const pg_tz *fc_tz)
{
	return fc_localsub(&fc_tz->state, fc_timep, &tm);
}


/*
 * gmtsub与gmtime的关系就像localsub与localtime的关系。
 *
 * 除了我们有一个私有的“struct state”用于GMT，因此不传递sp。
 */

static struct pg_tm * fc_gmtsub(pg_time_t const *fc_timep, int32 fc_offset,
	   struct pg_tm *fc_tmp)
{
	struct pg_tm *fc_result;

	/* GMT时区状态数据保存在这里 */
	static struct state *fc_gmtptr = NULL;

	if (fc_gmtptr == NULL)
	{
		/* 首次使用时分配 */
		fc_gmtptr = (struct state *) malloc(sizeof(struct state));
		if (fc_gmtptr == NULL)
			return NULL;		/* errno应通过malloc设置 */
		fc_gmtload(fc_gmtptr);
	}

	fc_result = fc_timesub(fc_timep, fc_offset, fc_gmtptr, fc_tmp);

	/*
	 * 这里可以花点心思，若偏移量非零则提供类似"+xx"或"-xx"的输出，
	 * 但这不是进行寻宝的时间。
	 */
	if (fc_offset != 0)
		fc_tmp->tm_zone = wildabbr;
	else
		fc_tmp->tm_zone = fc_gmtptr->chars;

	return fc_result;
}

struct pg_tm *
pg_gmtime(const pg_time_t *fc_timep)
{
	return fc_gmtsub(fc_timep, 0, &tm);
}

/*
 * 返回给定年份结束前的闰年数量
 * 其中，为了简化计算，年份零的答案定义为零。
 */

static int fc_leaps_thru_end_of_nonneg(int fc_y)
{
	return fc_y / 4 - fc_y / 100 + fc_y / 400;
}

static int fc_leaps_thru_end_of(const int fc_y)
{
	return (fc_y < 0
			? -1 - fc_leaps_thru_end_of_nonneg(-1 - fc_y)
			: fc_leaps_thru_end_of_nonneg(fc_y));
}

static struct pg_tm * fc_timesub(const pg_time_t *fc_timep, int32 fc_offset,
		const struct state *fc_sp, struct pg_tm *fc_tmp)
{
	const struct lsinfo *fc_lp;
	pg_time_t	fc_tdays;
	int			fc_idays;			/* 无符号类型会太2003 */
	int64		fc_rem;
	int			fc_y;
	const int  *fc_ip;
	int64		fc_corr;
	bool		fc_hit;
	int			fc_i;

	fc_corr = 0;
	fc_hit = false;
	fc_i = (fc_sp == NULL) ? 0 : fc_sp->leapcnt;
	while (--fc_i >= 0)
	{
		fc_lp = &fc_sp->lsis[fc_i];
		if (*fc_timep >= fc_lp->ls_trans)
		{
			fc_corr = fc_lp->ls_corr;
			fc_hit = (*fc_timep == fc_lp->ls_trans
				   && (fc_i == 0 ? 0 : fc_lp[-1].ls_corr) < fc_corr);
			break;
		}
	}
	fc_y = EPOCH_YEAR;
	fc_tdays = *fc_timep / SECSPERDAY;
	fc_rem = *fc_timep % SECSPERDAY;
	while (fc_tdays < 0 || fc_tdays >= year_lengths[isleap(fc_y)])
	{
		int			fc_newy;
		pg_time_t	fc_tdelta;
		int			fc_idelta;
		int			fc_leapdays;

		fc_tdelta = fc_tdays / DAYSPERLYEAR;
		if (!((!TYPE_SIGNED(pg_time_t) || INT_MIN <= fc_tdelta)
			  && fc_tdelta <= INT_MAX))
			goto out_of_range;
		fc_idelta = fc_tdelta;
		if (fc_idelta == 0)
			fc_idelta = (fc_tdays < 0) ? -1 : 1;
		fc_newy = fc_y;
		if (fc_increment_overflow(&fc_newy, fc_idelta))
			goto out_of_range;
		fc_leapdays = fc_leaps_thru_end_of(fc_newy - 1) -
			fc_leaps_thru_end_of(fc_y - 1);
		fc_tdays -= ((pg_time_t) fc_newy - fc_y) * DAYSPERNYEAR;
		fc_tdays -= fc_leapdays;
		fc_y = fc_newy;
	}

	/*
	 * 在给定范围内，我们现在可以无所畏惧地强制转换...
	 */
	fc_idays = fc_tdays;
	fc_rem += fc_offset - fc_corr;
	while (fc_rem < 0)
	{
		fc_rem += SECSPERDAY;
		--fc_idays;
	}
	while (fc_rem >= SECSPERDAY)
	{
		fc_rem -= SECSPERDAY;
		++fc_idays;
	}
	while (fc_idays < 0)
	{
		if (fc_increment_overflow(&fc_y, -1))
			goto out_of_range;
		fc_idays += year_lengths[isleap(fc_y)];
	}
	while (fc_idays >= year_lengths[isleap(fc_y)])
	{
		fc_idays -= year_lengths[isleap(fc_y)];
		if (fc_increment_overflow(&fc_y, 1))
			goto out_of_range;
	}
	fc_tmp->tm_year = fc_y;
	if (fc_increment_overflow(&fc_tmp->tm_year, -TM_YEAR_BASE))
		goto out_of_range;
	fc_tmp->tm_yday = fc_idays;

	/*
	 * 下方的“额外”修正避免了溢出问题。
	 */
	fc_tmp->tm_wday = EPOCH_WDAY +
		((fc_y - EPOCH_YEAR) % DAYSPERWEEK) *
		(DAYSPERNYEAR % DAYSPERWEEK) +
		fc_leaps_thru_end_of(fc_y - 1) -
		fc_leaps_thru_end_of(EPOCH_YEAR - 1) +
		fc_idays;
	fc_tmp->tm_wday %= DAYSPERWEEK;
	if (fc_tmp->tm_wday < 0)
		fc_tmp->tm_wday += DAYSPERWEEK;
	fc_tmp->tm_hour = (int) (fc_rem / SECSPERHOUR);
	fc_rem %= SECSPERHOUR;
	fc_tmp->tm_min = (int) (fc_rem / SECSPERMIN);

	/*
	 * 正的闰秒需要特殊的表示。这使用
	 * "... ??:59:60" 等等。
	 */
	fc_tmp->tm_sec = (int) (fc_rem % SECSPERMIN) + fc_hit;
	fc_ip = mon_lengths[isleap(fc_y)];
	for (fc_tmp->tm_mon = 0; fc_idays >= fc_ip[fc_tmp->tm_mon]; ++(fc_tmp->tm_mon))
		fc_idays -= fc_ip[fc_tmp->tm_mon];
	fc_tmp->tm_mday = (int) (fc_idays + 1);
	fc_tmp->tm_isdst = 0;
	fc_tmp->tm_gmtoff = fc_offset;
	return fc_tmp;

out_of_range:
	errno = EOVERFLOW;
	return NULL;
}

/*
 * 正常化逻辑由Paul Eggert提供。
 */

static bool fc_increment_overflow(int *fc_ip, int fc_j)
{
	int const	fc_i = *fc_ip;

	/*----------
	 * 如果 i >= 0，只有在 i + j > INT_MAX
	 * 或 j > INT_MAX - i 的情况下才可能发生溢出；考虑到 i >= 0，
	 * INT_MAX - i 不会溢出。
	 * 如果 i < 0，只有在 i + j < INT_MIN
	 * 或 j < INT_MIN - i 的情况下才可能发生溢出；考虑到 i < 0，
	 * INT_MIN - i 不会溢出。
	 *----------
	 */
	if ((fc_i >= 0) ? (fc_j > INT_MAX - fc_i) : (fc_j < INT_MIN - fc_i))
		return true;
	*fc_ip += fc_j;
	return false;
}

static bool fc_increment_overflow_time(pg_time_t *fc_tp, int32 fc_j)
{
	/*----------
	 * 这就像
	 * 'if (! (TIME_T_MIN <= *tp + j && *tp + j <= TIME_T_MAX)) ...',
	 * 只是即使 *tp + j 会溢出，它也能正确处理。
	 *----------
	 */
	if (!(fc_j < 0
		  ? (TYPE_SIGNED(pg_time_t) ? TIME_T_MIN - fc_j <= *fc_tp : -1 - fc_j < *fc_tp)
		  : *fc_tp <= TIME_T_MAX - fc_j))
		return true;
	*fc_tp += fc_j;
	return false;
}

static int64 fc_leapcorr(struct state const *fc_sp, pg_time_t fc_t)
{
	struct lsinfo const *fc_lp;
	int			fc_i;

	fc_i = fc_sp->leapcnt;
	while (--fc_i >= 0)
	{
		fc_lp = &fc_sp->lsis[fc_i];
		if (fc_t >= fc_lp->ls_trans)
			return fc_lp->ls_corr;
	}
	return 0;
}

/*
 * 查找给定时间后在给定时区内的下一个DST转换时间
 *
 * *timep和*tz是输入参数，其他参数是输出值。
 *
 * 当函数结果为1时，*boundary被设置为
 * *timep之后下一个DST转换时间的pg_time_t
 * 表示，*before_gmtoff和*before_isdst被设置为
 * 在该边界之前盛行的GMT偏移量和isdst
 * 状态（特别是在*timestamp之前的状态），
 * *after_gmtoff和*after_isdst被设置为
 * 在该边界之后盛行的状态。
 *
 * 当函数结果为0时，*timep之后没有已知的DST转换，
 * 但*before_gmtoff和*before_isdst指示
 * 在*timep时盛行的GMT偏移量和isdst状态。
 * （这可能发生在没有DST的时区，或者如果一个区域永久停止使用DST。）
 *
 * 函数结果为-1表示失败（在我们当前的实现中，此情况实际并不存在）。
 */
int pg_next_dst_boundary(const pg_time_t *fc_timep,
					 long int *fc_before_gmtoff,
					 int *fc_before_isdst,
					 pg_time_t *fc_boundary,
					 long int *fc_after_gmtoff,
					 int *fc_after_isdst,
					 const pg_tz *fc_tz)
{
	const struct state *fc_sp;
	const struct ttinfo *fc_ttisp;
	int			fc_i;
	int			fc_j;
	const pg_time_t fc_t = *fc_timep;

	fc_sp = &fc_tz->state;
	if (fc_sp->timecnt == 0)
	{
		/* 非DST区域，使用最低编号的标准类型 */
		fc_i = 0;
		while (fc_sp->ttis[fc_i].tt_isdst)
			if (++fc_i >= fc_sp->typecnt)
			{
				fc_i = 0;
				break;
			}
		fc_ttisp = &fc_sp->ttis[fc_i];
		*fc_before_gmtoff = fc_ttisp->tt_utoff;
		*fc_before_isdst = fc_ttisp->tt_isdst;
		return 0;
	}
	if ((fc_sp->goback && fc_t < fc_sp->ats[0]) ||
		(fc_sp->goahead && fc_t > fc_sp->ats[fc_sp->timecnt - 1]))
	{
		/* 对于转换表之外的值，进行外推 */
		pg_time_t	fc_newt = fc_t;
		pg_time_t	fc_seconds;
		pg_time_t	fc_tcycles;
		int64		fc_icycles;
		int			fc_result;

		if (fc_t < fc_sp->ats[0])
			fc_seconds = fc_sp->ats[0] - fc_t;
		else
			fc_seconds = fc_t - fc_sp->ats[fc_sp->timecnt - 1];
		--fc_seconds;
		fc_tcycles = fc_seconds / YEARSPERREPEAT / AVGSECSPERYEAR;
		++fc_tcycles;
		fc_icycles = fc_tcycles;
		if (fc_tcycles - fc_icycles >= 1 || fc_icycles - fc_tcycles >= 1)
			return -1;
		fc_seconds = fc_icycles;
		fc_seconds *= YEARSPERREPEAT;
		fc_seconds *= AVGSECSPERYEAR;
		if (fc_t < fc_sp->ats[0])
			fc_newt += fc_seconds;
		else
			fc_newt -= fc_seconds;
		if (fc_newt < fc_sp->ats[0] ||
			fc_newt > fc_sp->ats[fc_sp->timecnt - 1])
			return -1;			/* "无法发生" */

		fc_result = pg_next_dst_boundary(&fc_newt, fc_before_gmtoff,
									  fc_before_isdst,
									  fc_boundary,
									  fc_after_gmtoff,
									  fc_after_isdst,
									  fc_tz);
		if (fc_t < fc_sp->ats[0])
			*fc_boundary -= fc_seconds;
		else
			*fc_boundary += fc_seconds;
		return fc_result;
	}

	if (fc_t >= fc_sp->ats[fc_sp->timecnt - 1])
	{
		/* 没有已知的转换 > t，因此使用最后已知段的类型 */
		fc_i = fc_sp->types[fc_sp->timecnt - 1];
		fc_ttisp = &fc_sp->ttis[fc_i];
		*fc_before_gmtoff = fc_ttisp->tt_utoff;
		*fc_before_isdst = fc_ttisp->tt_isdst;
		return 0;
	}
	if (fc_t < fc_sp->ats[0])
	{
		/* 对于 "before"，使用编号最低的标准类型 */
		fc_i = 0;
		while (fc_sp->ttis[fc_i].tt_isdst)
			if (++fc_i >= fc_sp->typecnt)
			{
				fc_i = 0;
				break;
			}
		fc_ttisp = &fc_sp->ttis[fc_i];
		*fc_before_gmtoff = fc_ttisp->tt_utoff;
		*fc_before_isdst = fc_ttisp->tt_isdst;
		*fc_boundary = fc_sp->ats[0];
		/* 而对于 "after"，使用第一个段的类型 */
		fc_i = fc_sp->types[0];
		fc_ttisp = &fc_sp->ttis[fc_i];
		*fc_after_gmtoff = fc_ttisp->tt_utoff;
		*fc_after_isdst = fc_ttisp->tt_isdst;
		return 1;
	}
	/* 否则搜索以找到 t 之后的边界 */
	{
		int			fc_lo = 1;
		int			fc_hi = fc_sp->timecnt - 1;

		while (fc_lo < fc_hi)
		{
			int			fc_mid = (fc_lo + fc_hi) >> 1;

			if (fc_t < fc_sp->ats[fc_mid])
				fc_hi = fc_mid;
			else
				fc_lo = fc_mid + 1;
		}
		fc_i = fc_lo;
	}
	fc_j = fc_sp->types[fc_i - 1];
	fc_ttisp = &fc_sp->ttis[fc_j];
	*fc_before_gmtoff = fc_ttisp->tt_utoff;
	*fc_before_isdst = fc_ttisp->tt_isdst;
	*fc_boundary = fc_sp->ats[fc_i];
	fc_j = fc_sp->types[fc_i];
	fc_ttisp = &fc_sp->ttis[fc_j];
	*fc_after_gmtoff = fc_ttisp->tt_utoff;
	*fc_after_isdst = fc_ttisp->tt_isdst;
	return 1;
}

/*
 * 确定给定时区中时区缩写的含义
 *
 * 确定与该缩写相关的 GMT 偏移和夏令时标志。
 * 这通常仅在缩写实际上随时间改变了含义时使用；因此，我们还取一个 UTC 截止时间，并返回
 * 在该时间或最接近该时间之前使用的含义，或者如果该缩写在该时间之前从未使用过，则返回
 * 该时间之后首次使用的含义。
 *
 * 成功时返回 true，并设置 *gmtoff 和 *isdst。如果该缩写在该时区根本没有使用过，则返回 false。
 *
 * 注意：abbrev 是大小写敏感匹配的；应该全为大写。
 */
bool pg_interpret_timezone_abbrev(const char *fc_abbrev,
							 const pg_time_t *fc_timep,
							 long int *fc_gmtoff,
							 int *fc_isdst,
							 const pg_tz *fc_tz)
{
	const struct state *fc_sp;
	const char *fc_abbrs;
	const struct ttinfo *fc_ttisp;
	int			fc_abbrind;
	int			fc_cutoff;
	int			fc_i;
	const pg_time_t fc_t = *fc_timep;

	fc_sp = &fc_tz->state;

	/*
	 * 在时区的缩写列表中定位缩写。我们假设
	 * 列表中没有重复项。
	 */
	fc_abbrs = fc_sp->chars;
	fc_abbrind = 0;
	while (fc_abbrind < fc_sp->charcnt)
	{
		if (strcmp(fc_abbrev, fc_abbrs + fc_abbrind) == 0)
			break;
		while (fc_abbrs[fc_abbrind] != '\0')
			fc_abbrind++;
		fc_abbrind++;
	}
	if (fc_abbrind >= fc_sp->charcnt)
		return false;			/* 不在那里！ */

	/*
	 * 与 pg_next_dst_boundary 不同，我们不需要担心外推
	 * （回退/前进区域）。找到缩写的最新或最旧含义应该能得到我们想要的，
	 * 因为外推只会重复最新或最旧的含义。
	 *
	 * 使用二分查找来定位第一个转换 > 截止时间。
	 */
	{
		int			fc_lo = 0;
		int			fc_hi = fc_sp->timecnt;

		while (fc_lo < fc_hi)
		{
			int			fc_mid = (fc_lo + fc_hi) >> 1;

			if (fc_t < fc_sp->ats[fc_mid])
				fc_hi = fc_mid;
			else
				fc_lo = fc_mid + 1;
		}
		fc_cutoff = fc_lo;
	}

	/*
	 * 向后扫描以找到截止时间之前使用给定缩写的最新区间。
	 */
	for (fc_i = fc_cutoff - 1; fc_i >= 0; fc_i--)
	{
		fc_ttisp = &fc_sp->ttis[fc_sp->types[fc_i]];
		if (fc_ttisp->tt_desigidx == fc_abbrind)
		{
			*fc_gmtoff = fc_ttisp->tt_utoff;
			*fc_isdst = fc_ttisp->tt_isdst;
			return true;
		}
	}

	/*
	 * 不在那儿，因此向前扫描以找到截止时间之后的第一个。
	 */
	for (fc_i = fc_cutoff; fc_i < fc_sp->timecnt; fc_i++)
	{
		fc_ttisp = &fc_sp->ttis[fc_sp->types[fc_i]];
		if (fc_ttisp->tt_desigidx == fc_abbrind)
		{
			*fc_gmtoff = fc_ttisp->tt_utoff;
			*fc_isdst = fc_ttisp->tt_isdst;
			return true;
		}
	}

	return false;				/* 嗯，实际上在任何区间中都没有使用？ */
}

/*
 * 如果给定时区仅使用一个 GMT 偏移，将该偏移存储到 *gmtoff 中并返回 true，否则返回 false。
 */
bool pg_get_timezone_offset(const pg_tz *fc_tz, long int *fc_gmtoff)
{
	/*
	 * 如果历史上使用了多个缩写，该时区可能会有多个 ttinfo。
	 * 只要它们都有相同的 gmtoff，我们返回 true。
	 */
	const struct state *fc_sp;
	int			fc_i;

	fc_sp = &fc_tz->state;
	for (fc_i = 1; fc_i < fc_sp->typecnt; fc_i++)
	{
		if (fc_sp->ttis[fc_i].tt_utoff != fc_sp->ttis[0].tt_utoff)
			return false;
	}
	*fc_gmtoff = fc_sp->ttis[0].tt_utoff;
	return true;
}

/*
 * 返回当前时区的名称
 */
const char * pg_get_timezone_name(pg_tz *fc_tz)
{
	if (fc_tz)
		return fc_tz->TZname;
	return NULL;
}

/*
 * 检查时区是否可接受。
 *
 * 我们在这里所做的是检查对闰秒的时刻管理。
 * 我们需要拒绝这样的 TZ 设置，因为它们会对我们的日期/时间运算造成严重影响。
 */
bool pg_tz_acceptable(pg_tz *fc_tz)
{
	struct pg_tm *fc_tt;
	pg_time_t	fc_time2000;

	/*
	 * 为了检测闰秒时刻管理，运行 pg_localtime 来检查应该是
	 * GMT 午夜，2000-01-01。坚持要求 tm_sec 值为零；任何其他结果都需归因于闰秒。
	 */
	fc_time2000 = (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY;
	fc_tt = pg_localtime(&fc_time2000, fc_tz);
	if (!fc_tt || fc_tt->tm_sec != 0)
		return false;

	return true;
}
